# !/usr/bin/python2.7
# -*- coding: UTF-8 -*-

#https://github.com/IanHarvey/bluepy/blob/master/bluepy

import os
import sys
import shutil
import time
import datetime
import binascii
import bluepy
import struct


class bluedev(object):
    conn = None
    def __init__(self,hcix=0,mtu=250):
        self.hcix = hcix
        self.mtu = mtu
        self.checkdev()

    def __enter__(self):
        return self

    def __exit__(self):
        bluedev.conn.disconnect()
    
    def checkdev(self):
        dev=os.popen('hcitool dev').read()
        if dev.count('hci')==0:
            print 'No local bluetooth device'
            exit()

    def scan(self,scandelegate,timeout=0):
        scanner = bluepy.btle.Scanner(self.hcix).withDelegate(scandelegate)
        devices = scanner.scan(timeout)
        return devices

    def connect(self,addr,addrtype='public'):
        if not addr is None and bluedev.conn is None:
            bluedev.targetmac = addr
            bluedev.targetmactype = addrtype
            bluedev.conn = bluepy.btle.Peripheral(addr,addrtype)
            bluedev.conn.services = bluedev.conn.discoverServices().values()
            if bluedev.conn is not None:
                bluedev.conn.setMTU(self.mtu)
        return bluedev.conn

    def discovery_services(self):
        if bluedev.conn is None:
            return None
        #services = [{'serve':[],'char':['uuid':[],'hnd':[],'sups':[],'val':[]]}]
        services = []
        for svc in bluedev.conn.services:
            #print(str(svc), ":")
            services.append({'serve':str(svc).split('<')[-1].split('>')[0]})
            services[-1]['char'] = []
            for ch in svc.getCharacteristics():
                #print "    ",("({}, hnd={}, supports {})".format(ch, hex(ch.handle), ch.propertiesToString()))
                #chName = bluepy.btle.AssignedNumbers.getCommonName(ch.uuid)
                services[-1]['char'].append({'uuid':"{}".format(ch).split('<')[-1].split('>')[0],'hnd':hex(ch.handle),'sups':ch.propertiesToString()})
                if (ch.supportsRead()):
                    try:
                        #print "        ",("->", repr(ch.read()))
                        services[-1]['char'][-1]['val'] = ch.read()
                    except BTLEException as e:
                        #print "        ",("->", e)
                        pass
        return services

    def getcharhandleByUUID(self,serveuuid=None,charuuid=None):
        if serveuuid is None or charuuid is None:
            return None 
        svc = bluedev.conn.getServiceByUUID(serveuuid)
        ch = svc.getCharacteristics()  
        for charhandle in ch: 
            if (bluepy.btle.UUID(charuuid) == charhandle.uuid): 
                return charhandle
        return None

    def char_notify(self,charhandle=None):
        if charhandle is None:
            return False
        if "NOTIFY" in charhandle.propertiesToString().upper():
            #Turn on notifications
            bluedev.conn.writeCharacteristic(charhandle.getHandle() + 1,struct.pack('<bb', 0x01, 0x00))
            return True
        else:
            return False

    def wait_notify(self,timeout=0):
        resp = bluedev.conn._getResp(['ntfy'],timeout)
        return resp

    def wait_indicate(self,timeout=0):
        resp = bluedev.conn._getResp(['ind'],timeout)
        return resp

    def read_char(self,charhandle=None,timeout=1):
        if not charhandle is None:
            supports = charhandle.propertiesToString().upper();
            if "READ" in supports:
                read = bluedev.conn.readCharacteristic(charhandle)
                return read
                #print "read-> ",("->",read),map(ord,read)
            elif "NOTIFY" in supports:
                #res = conn.waitForNotifications(1)
                savedStdout = sys.stdout  #保存标准输出流
                with open('notifylog.log', 'w+') as file:
                    try:
                        sys.stdout = file  #标准输出重定向至文件
                        res = self.wait_notify(timeout)
                        sys.stdout = savedStdout  #恢复标准输出流
                        if res != None:
                            return res
                        #print "notify-> ",res['d']
                    except:
                        sys.stdout = savedStdout  #恢复标准输出流
                        return None

            elif "INDICATE" in supports:
                #res = conn.waitForNotifications(1)
                savedStdout = sys.stdout  #保存标准输出流
                with open('notifylog.log', 'w+') as file:
                    try:
                        sys.stdout = file  #标准输出重定向至文件
                        res = self.wait_indicate(timeout)
                        sys.stdout = savedStdout  #恢复标准输出流
                        if res != None:
                            return res
                    except:
                        sys.stdout = savedStdout  #恢复标准输出流
                        return None
            else:
                return None
        else:
            #print "Not find target characteristic"
            return None

    def write_char(self,charhandle=None,senddata=None,resp=False):
        if not charhandle is None:
            supports = charhandle.propertiesToString().upper();
            if "WRITE" not in supports:
                return None
            if not senddata is None:
                #print "write-> ","\"",senddata,"\"",",","\"",binascii.b2a_hex(senddata).decode('utf-8'),"\""
                bluedev.conn.writeCharacteristic(charhandle.getHandle(),senddata,resp)
        else:
            #print "Not find target characteristic"
            return None

    def disconnect(self):
        if bluedev.conn is None:
            return False
        bluedev.conn.disconnect()
        return True

class ScanDelegate(bluepy.btle.DefaultDelegate):

    def __init__(self,scandev = True,scandata = True,whitelist="",save2file = False,disp=True):
        bluepy.btle.DefaultDelegate.__init__(self)
        self.whitelist = whitelist
        self.save2file = save2file 
        self.scandev = scandev 
        self.scandata = scandata
        self.disp = disp
        self.__mklogdir()

    def __mklogdir(self):
        if not self.save2file is None and not os.path.exists(r'./log/'):
            os.makedirs(r'./log/')

    def handleDiscovery(self, dev, isNewDev, isNewData):
        if self.scandev:
            #if isNewDev:
            if self.disp:
                print "Discovered device:", dev.addr,dev.scanData.get(8,"No name")
        if self.scandata:
            if isNewDev:
                pass 
            else:#elif isNewData:
                if (len(self.whitelist) == 0) or (dev.addr.upper() in (item.upper() for item in self.whitelist)):
                    if self.save2file:
                        fo = open("./log/" + dev.addr.upper().replace(":","_",5)+".log", "ab")
                        #写入文件时间戳
                        fo.write("time %s " % datetime.datetime.now())
                        #写入文件MAC
                        fo.write("mac %s " % dev.addr)
                        #写入文件信号强度
                        fo.write("rssi %d " % dev.rssi)
                        #写入数据
                        fo.write("data ")

                    if self.disp:
                        print "Received new data from mac", dev.addr.upper(),"time",datetime.datetime.now(),"rssi %d" % dev.rssi,"data",

                    #for i in map(ord,(dev.scanData[255])):#此处只打印自定义数据
                    #    print "%02X " % i,
                    #    if self.save2file: 
                    #        fo.write("%02X " % i)

                    for keys in dev.scanData:
                        if self.disp:
                            print "%02X %02X" % (1+len(dev.scanData[keys]),int(keys)),
                        if self.save2file: 
                            fo.write("%02X %02X " % (1+len(dev.scanData[keys]),int(keys)))
                        for i in map(ord,dev.scanData[keys]):
                            if self.disp:
                                print "%02X" % (i),
                            if self.save2file: 
                                fo.write("%02X " % i)
                    if self.disp:
                        print ""
                    if self.save2file:
                        fo.write("\r\n")
                        fo.close()

def printusage():
    print "Usage:"
    print "     sudo python mblue.py lescan/ledump/[connect xx:xx:xx:xx:xx:xx] [hcix to select blue dev] [-h help]"
    return;


if __name__ == '__main__':

    if "-h" in sys.argv:
        printusage()  
        exit()
    if 'hci' in ''.join(sys.argv):
        agv=''.join(sys.argv)
        hci = int(agv[agv.index('hci')+3])
    else:
        hci = 0
    if "lescan" in sys.argv:
        bluedev(hcix=hci).scan(ScanDelegate(True,False,whitelist="",save2file=False,disp=True))
    elif "ledump" in sys.argv:
        bluedev(hcix=hci).scan(ScanDelegate(False,True,whitelist="",save2file=False,disp=True))
    elif "connect" in sys.argv:
        mac = sys.argv[(sys.argv.index("connect") + 1)]        
 
        if len(mac) is not 17:
            print 'mac error'
            exit()
        dev = bluedev(hcix=hci)
        print "connect to %s" % mac
        try:
            dev.connect(mac)
        except:
            print "con not connect"
            exit()
        serve = dev.discovery_services()
        for i in range(0,len(serve)):
            print "service-> {%s}" % (serve[i]['serve'])
            for j in range(0,len(serve[i]['char'])):
                print "    characteristic-> ",serve[i]['char'][j]

        chand = dev.getcharhandleByUUID('1101','8888')
        if chand is not None:
            print "discovery 1101 8888 char"
            if dev.char_notify(chand) is True:
                print "turn on notify"
            print "writing..."
            dev.write_char(chand,"1234567890\r\n",True)
            while True:
                read=dev.read_char(chand)
                if read is not None:
                    print str(read)
            dev.disconnect()
            print "close connect"
    else:
        printusage()  







